package drds.plus.sql_process.abstract_syntax_tree.node.dml;

import drds.plus.common.jdbc.Parameters;
import drds.plus.sql_process.abstract_syntax_tree.ObjectCreateFactory;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.dml.IPut;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.node.query.TableQuery;
import drds.plus.sql_process.abstract_syntax_tree.node.query.TableQueryWithIndex;
import drds.plus.sql_process.optimizer.OptimizerContext;
import drds.plus.sql_process.rule.RouteOptimizer;
import drds.plus.sql_process.utils.OptimizerUtils;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class Insert extends Dml<Insert> {
    @Setter
    @Getter
    private List<Item> duplicateUpdateColumnList;
    @Setter
    @Getter
    private List<Object> duplicateUpdateValueList;

    public Insert(TableQuery tableQueryNode) {
        super(tableQueryNode);
    }

    public IPut toExecutePlan(int shareIndex) {
        drds.plus.sql_process.abstract_syntax_tree.execute_plan.dml.Insert insert = ObjectCreateFactory.createInsert();
        insert.setConsistent(true);
        //
        if (this.getDuplicateUpdateColumnList() != null) {
            List<String> shardColumnNameList = OptimizerContext.getOptimizerContext().getRouteOptimizer().getShardColumnNameList(this.getWhere().getTableMetaData().getTableName());
            for (Item item : this.getDuplicateUpdateColumnList()) {
                if (shardColumnNameList.contains(item.getColumnName())) {
                    throw new IllegalArgumentException("column :" + item.getColumnName() + " is partition columnName , can't be modify");
                }
                if (this.getWhere().getTableMetaData().getPrimaryKeyKeyColumnNameToColumnMetaDataMap().containsKey(item.getColumnName())) {
                    throw new IllegalArgumentException("column :" + item.getColumnName() + " is primary columnName , can't be modify");
                }
            }
        }

        if (this.getWhere().getActualTableName() != null) {
            insert.setTableName(this.getWhere().getActualTableName());
        } else if (this.getWhere() instanceof TableQueryWithIndex) {
            insert.setTableName(((TableQueryWithIndex) this.getWhere()).getIndexName());
        } else {
            insert.setTableName(this.getWhere().getTableName());
        }
        insert.setIndexName((this.getWhere()).getIndexMappingUsed().getIndexName());
        //
        insert.setExistSequenceValue(this.isExistSequenceValue());
        insert.setUpdateItemList(this.getColumnNameList());
        insert.setUpdateValues(this.getColumnValueList());
        if (this.getQuery() != null) {
            insert.setQueryTree((Query) this.getQuery().toExecutePlan());
        }
        insert.setIsValueListList(this.isValueListList());
        insert.setValueListList(this.getValueListList());
        insert.setDuplicateUpdateColumns(this.getDuplicateUpdateColumnList());
        insert.setDuplicateUpdateValues(this.getDuplicateUpdateValueList());
        //
        insert.setDataNodeId(this.getWhere().getDataNodeId());
        insert.setBatchIndexList(this.getBatchIndexList());
        return insert;
    }

    public Insert deepCopy() {
        Insert insertNode = new Insert(null);
        super.deepCopySelfTo(insertNode);
        insertNode.setDuplicateUpdateColumnList(OptimizerUtils.copySelectItemList(this.getDuplicateUpdateColumnList()));
        insertNode.setDuplicateUpdateValueList(OptimizerUtils.copyValues(this.getDuplicateUpdateValueList()));
        return insertNode;
    }

    public Insert copy() {
        Insert insertNode = new Insert(null);
        super.copySelfTo(insertNode);
        insertNode.setIsValueListList(this.isValueListList());
        insertNode.setValueListList(getValueListList());
        insertNode.setDuplicateUpdateColumnList(this.getDuplicateUpdateColumnList());
        insertNode.setDuplicateUpdateValueList(this.getDuplicateUpdateValueList());
        return insertNode;
    }


    public void duplicateUpdate(String[] updateColumns, Object[] updateValues) {
        List<Item> itemList = new LinkedList<Item>();
        for (String name : updateColumns) {
            Item s = OptimizerUtils.createColumnFromString(name);
            itemList.add(s);
        }

        List<Object> valueList = new ArrayList<Object>(Arrays.asList(updateValues));

        this.setDuplicateUpdateColumnList(itemList);
        this.setDuplicateUpdateValueList(valueList);

    }


    public void assignment(Parameters parameters) {
        super.assignment(parameters);
        if (duplicateUpdateValueList != null) {//进行赋值
            this.setDuplicateUpdateValueList(assignmentValues(duplicateUpdateValueList, parameters));
        }
    }

    public void build() {
        super.build();
        if (this.getDuplicateUpdateColumnList() != null) {
            for (Item item : this.getDuplicateUpdateColumnList()) {
                Item $item = null;
                for (Item item1 : where.copyReferedItemList()) {
                    if (item.isSameColumnName(item1)) { // 尝试查找对应的字段
                        $item = item1;
                        break;
                    }
                }
                if ($item == null) {
                    throw new IllegalArgumentException("column: " + item.getColumnName() + " is not existed in " + where.getName());
                }
                item.setTableName($item.getTableName());
                item.setType($item.getType());
                item.setAutoIncrement($item.isAutoIncrement());
            }
            //类型转换
            convertByValueType(this.getDuplicateUpdateColumnList(), this.getDuplicateUpdateValueList());
        }
    }


    public boolean processAutoIncrement() {
        if (!super.processAutoIncrement()) {
            return false;
        }
        String tableName = getTableMetaData().getTableName();
        RouteOptimizer routeOptimizer = OptimizerContext.getOptimizerContext().getRouteOptimizer();
        return !routeOptimizer.isTableInSingleDb(tableName) || routeOptimizer.isBroadCast(tableName);//单库单表才或者广播表才允许自增
    }
}
